package websocket

import (
	"github.com/gorilla/websocket"
	"net/http"
		"bytes"
	"compress/gzip"
	"errors"
	"github.com/sirupsen/logrus"
	"fmt"
	"time"
)

var CtxHashMap = ConnHashMap{}

type WebSocketHandler struct {
	Wsupgrader websocket.Upgrader
}

func (wsh *WebSocketHandler) NewWebSocketHandler() {
	wsh.Wsupgrader = websocket.Upgrader{
		ReadBufferSize:  4096,
		WriteBufferSize: 4096,
	}
}

func (wsh *WebSocketHandler) onMessage(conn *websocket.Conn, ctx *ConnContext, msg []byte, msgType int) {
	//处理文本消息 或者 2进制消息 2进制通常是些 gzip的文本 语音或者图片视频之类的一般会用其他云服务不然带宽会爆掉
	if msgType == websocket.TextMessage {
		wsh.processIncomingTextMsg(conn, ctx, msg)
	}
	if msgType == websocket.BinaryMessage {

	}
}

func (wsh *WebSocketHandler) onOpen(conn *websocket.Conn, r *http.Request) (ctx *ConnContext, err error) {
	if err := r.ParseForm(); err != nil {
		return nil, errors.New("参数校验错误")
	}
	specialKey := r.FormValue("specialKey")
	supportGzip := r.FormValue("support_gzip")

	ctx = &ConnContext{specialKey, supportGzip}
	//用来标识一个tcp链接
	keyString := ctx.AsHashKey()

	if oldConn, ok := CtxHashMap.Get(keyString); ok {
		wsh.onClose(oldConn, ctx)
		oldConn.Close()
	}
	//CtxHashMap[keyString] = conn
	CtxHashMap.Set(keyString,conn)
	fmt.Println("ctx", ctx)
	return ctx, nil
}

func (wsh *WebSocketHandler) onClose(conn *websocket.Conn, ctx *ConnContext) {
	logrus.Info("client close itself as " + ctx.String())
	wsh.closeConnWithCtx(ctx)
	return
}

func (wsh *WebSocketHandler) onError(errMsg string) {
	logrus.Error(errMsg)
}
func (wsh *WebSocketHandler) HandleConn(w http.ResponseWriter, r *http.Request) {
	wsh.Wsupgrader.CheckOrigin = func(r *http.Request) bool { return true }
	conn, err := wsh.Wsupgrader.Upgrade(w, r, nil)
	if err != nil {
		logrus.Error("Failed to set websocket upgrade: " + err.Error())
		return
	}
	defer conn.Close()
	if ctx, err := wsh.onOpen(conn, r); err != nil {
		logrus.Error("Open connection failed " + err.Error() + r.URL.RawQuery)
		return
	} else {
		conn.SetPingHandler(func(message string) error {
			conn.WriteControl(websocket.PongMessage, []byte(message), time.Now().Add(time.Second))
			return nil
		})
		for {
			t, msg, err := conn.ReadMessage()
			if err != nil {
				logrus.Error("READ ERR FROM " + ctx.String() + " ERR " + err.Error())
				wsh.onClose(conn, ctx)
				return
			}

			switch t {
			case websocket.TextMessage, websocket.BinaryMessage:
				wsh.onMessage(conn, ctx, msg, t)
			case websocket.CloseMessage:
				wsh.onClose(conn, ctx)
				return
			case websocket.PingMessage:
			case websocket.PongMessage:
			}

		}
	}
}

func (wsh *WebSocketHandler) closeConnWithCtx(ctx *ConnContext) {
	keyString := ctx.AsHashKey()
	CtxHashMap.Delete(keyString)
	return
}
func (wsh *WebSocketHandler) processIncomingTextMsg(conn *websocket.Conn, ctx *ConnContext, msg []byte) {
	logrus.Info("CLIENT SAID " + string(msg))
	wsh.sendMessageToAll(msg)
}

func (wsh *WebSocketHandler) sendMessageToAll(msg []byte) {
	var gzMsg bytes.Buffer
	gzWriter := gzip.NewWriter(&gzMsg)
	gzWriter.Write(msg)
	gzWriter.Flush()
	gzWriter.Close()
	for key, conn := range CtxHashMap.Items() {
		if ctx, err := HashKeyAsCtx(key); err != nil {
			wsh.onError(err.Error())
		} else {
			if ctx.supportGzip == "1" {
				err = conn.WriteMessage(websocket.BinaryMessage, gzMsg.Bytes())
				logrus.Info("send binary msg to " + ctx.String())
			} else {
				err = conn.WriteMessage(websocket.TextMessage, []byte(msg))
				logrus.Info("send text msg to " + ctx.String())
			}
			if err != nil {
				wsh.onClose(conn, ctx)
				conn.Close()
				wsh.onError("WRITE ERR TO " + key + " ERR:" + err.Error())
			}
		}
	}

}

func SendMsgToClient(conn *websocket.Conn, msg []byte) (err error) {
	err = conn.WriteMessage(websocket.TextMessage, []byte(msg))
	return
}

func GetWebSocketConn(key string) (*websocket.Conn, error) {
	if conn, ok := CtxHashMap.Get(key); ok {
		return conn, nil
	}
	return nil,errors.New("key is not exists conn")
}
